package dao

import (
	"context"
	"sale-admin/internal/app/grpc/client"
	"sale-admin/internal/app/grpc/protoc/system_admin"
	"sale-admin/internal/dal"
	"sale-admin/internal/define"
	"sale-admin/internal/models"
	"strings"
	"time"

	"192.168.1.75/go-pkg/errorx"
)

// CouponPatch 优惠码批次
type CouponBatch struct{}

// Exists 判断批次是否存在
func (CouponBatch) Exists(ctx context.Context, query *dal.Query, batchID string) bool {
	cp := query.CouponBatch
	count, _ := cp.WithContext(ctx).Where(cp.ID.Eq(batchID)).Count()
	return count == 1
}

// Add 新增批次
func (CouponBatch) Create(
	ctx context.Context,
	query *dal.Query,
	record *models.CouponBatch,
) (ID string, err error) {
	cb := query.CouponBatch
	err = cb.WithContext(ctx).Create(record)
	if err != nil {
		return "", err
	}
	return record.ID, nil
}

// Update 更新批次
func (CouponBatch) Update(
	ctx context.Context,
	query *dal.Query,
	ID string, // 批次id
	name string, // 批次名称
	totalTimes int64, // 每个优惠码可使用的次数，-1: 不限制
	validityStartTime, validityEndTime int64, // 有效期开始时间-结束时间
	orderAmountRequired int, // 使用优惠码，是否有金额限制 1:限制 2:不限制
	orderAmount float64, // 使用优惠码，需满足的订单金额
	reduce float64, // 满减的额度
	discount float64, // 打折的额度
	remark, desc string, // 备注，详情
	merchantIDs []string, //直系商户组
) error {
	cb := query.CouponBatch
	tx := cb.WithContext(ctx).
		Where(cb.ID.Eq(ID))
	if len(merchantIDs) > 0 {
		tx = tx.Where(cb.MerchantID.In(merchantIDs...))
	}
	res, _ := tx.Updates(&models.CouponBatch{
		Name:                name,
		TotalTimes:          totalTimes,
		ValidityStartTime:   validityStartTime,
		ValidityEndTime:     validityEndTime,
		OrderAmountRequired: orderAmountRequired,
		OrderAmount:         orderAmount,
		Reduce:              reduce,
		Discount:            discount,
		Remark:              remark,
		Desc:                desc,
	})
	// 涉及到零值更新，单独更新优惠和折扣额度
	if reduce > 0 {
		cb.WithContext(ctx).
			Where(cb.ID.Eq(ID)).
			Where(cb.MerchantID.In(merchantIDs...)).
			UpdateSimple(cb.Discount.Value(0.00))
	} else {
		cb.WithContext(ctx).
			Where(cb.ID.Eq(ID)).
			Where(cb.MerchantID.In(merchantIDs...)).
			UpdateSimple(cb.Reduce.Value(0.00))
	}
	if res.RowsAffected <= 0 {
		return errorx.New("更新失败", -1)
	}
	return nil
}

// UpdateTotalNum 更新发放数量
func (CouponBatch) UpdateTotalNum(
	ctx context.Context,
	query *dal.Query,
	batchID string,
	num int64, // 新增的发放量
) error {
	cb := query.CouponBatch
	res, _ := cb.WithContext(ctx).Where(cb.ID.Eq(batchID)).UpdateSimple(
		cb.TotalNum.Add(num),
	)
	if res.RowsAffected <= 0 {
		return errorx.New("更新库存失败")
	}
	return nil
}

// SumTotalUsedNum 汇总总的使用量
func (CouponBatch) TotalUsedNum(
	ctx context.Context,
	query *dal.Query,
	batchID string,
) int64 {
	var totalUsedNum int64
	cc := query.CouponCode
	totalUsedNum, _ = cc.WithContext(ctx).
		Where(cc.BatchID.Eq(batchID)).
		Where(cc.Status.Eq(int(define.CouponChildCodeStatus_Used))).
		Count()
	return totalUsedNum
}

// 优惠码批次详情
func (CouponBatch) Detail(
	ctx context.Context,
	query *dal.Query,
	ID string, // 批次id
) (*models.CouponBatch, error) {
	cb := query.CouponBatch
	record, err := cb.WithContext(ctx).Where(cb.ID.Eq(ID)).Take()
	if err != nil || record == nil {
		return nil, errorx.New("无效的批次", -1)
	}
	return record, nil
}

// List 批次列表
func (CouponBatch) List(
	ctx context.Context,
	query *dal.Query,
	name string, // 批次名称
	codeType int, // 批次类型
	status define.StatusType, // 批次状态
	merchantID []string, // 所属商户
	page, limit int, // 分页参数
) (
	records []*models.CouponBatch,
	count int64,
	err error,
) {
	cb := query.CouponBatch
	tx := cb.WithContext(ctx)
	name = strings.TrimSpace(name)
	if name != "" {
		tx = tx.Where(cb.Name.Like("%" + name + "%"))
	}
	if codeType > 0 {
		tx = tx.Where(cb.CodeType.Eq(codeType))
	}
	if status > 0 {
		tx = tx.Where(cb.Status.Eq(int(status)))
	}
	if len(merchantID) > 0 {
		tx = tx.Where(cb.MerchantID.In(merchantID...))
	}
	count, _ = tx.Count()

	if page > 0 {
		tx = tx.Offset((page - 1) * limit)
	}
	if limit > 0 {
		tx = tx.Limit(limit)
	}
	records, err = tx.Order(cb.CreatedAt.Desc()).Find()
	for index, record := range records {
		if record.ValidityEndTime < time.Now().Unix() {
			records[index].Status = int(define.CouponBatchStatus_Invalid)
		}
	}
	return
}

// GetByID 通过id获取优惠码批次记录
func (CouponBatch) FindByIDs(ctx context.Context, query *dal.Query, batchIDs []string) ([]*models.CouponBatch, error) {
	cp := query.CouponBatch
	return cp.WithContext(ctx).Where(cp.ID.In(batchIDs...)).Order(cp.CreatedAt.Desc()).Find()
}

type CountBatchOptionType struct {
	ID   string `json:"id" gorm:"column:id"`
	Name string `json:"name" gorm:"column:name"`
}

// Options 批次下拉选项列表
func (CouponBatch) Options(
	ctx context.Context,
	query *dal.Query,
	codeType int, // 优惠码类型
	merchantID string, // 所属商户
) (records []CountBatchOptionType) {
	cli, err := client.GetSystemAdminClient()
	if err != nil {
		return []CountBatchOptionType{}
	}
	rsp, err := cli.GetFamilyMerchants(ctx, &system_admin.FamilyMerchantsReq{
		MerchantId: merchantID,
	})
	if err != nil || rsp == nil {
		return []CountBatchOptionType{}
	}
	cb := query.CouponBatch
	tx := cb.WithContext(ctx)
	if merchantID != "1" {
		tx = tx.Where(cb.MerchantID.In(rsp.MerchantIds...))
	}
	if codeType > 0 {
		tx = tx.Where(cb.CodeType.Eq(codeType))
	}
	// 按创建时间降序
	tx = tx.Order(cb.CreatedAt.Desc())
	tx.Scan(&records)
	return
}

// Invalid 使批次失效
func (cp CouponBatch) Invalid(ctx context.Context, query *dal.Query, batchID string, merchantID []string) error {
	// 先让批次失效
	cb := query.CouponBatch
	tx := cb.WithContext(ctx).
		Where(cb.ID.Eq(batchID))
	if len(merchantID) > 0 {
		tx = tx.Where(cb.MerchantID.In(merchantID...))
	}
	res, _ := tx.UpdateSimple(
		cb.Status.Value(int(define.CouponBatchStatus_Invalid)),
	)
	if res.RowsAffected <= 0 {
		return errorx.New("操作失败", -1)
	}
	// 再让码失效
	cc := query.CouponCode
	cc.WithContext(ctx).
		Where(cc.BatchID.Eq(batchID)).
		UpdateSimple(
			cc.Status.Value(int(define.CouponCodeStatus_Invalid)),
		)
	return nil
}

// CouponCode
type CouponCode struct{}

// Exists 优惠码是否存在
func (CouponCode) Exists(ctx context.Context, query *dal.Query, code string) bool {
	cc := query.CouponCode
	count, _ := cc.WithContext(ctx).Where(cc.Code.Eq(code)).Count()
	return count >= 1
}

// Create 生成主码
// 支持批量创建
func (CouponCode) Create(
	ctx context.Context,
	query *dal.Query,
	records []*models.CouponCode,
) error {
	cc := query.CouponCode
	err := cc.WithContext(ctx).CreateInBatches(records, 500)
	return err
}

// Disable 使失效
func (CouponCode) Disable(
	ctx context.Context,
	query *dal.Query,
	code string,
) error {
	cc := query.CouponCode
	res, _ := cc.WithContext(ctx).
		Where(cc.Code.Eq(code)).
		UpdateSimple(
			cc.Status.Value(int(define.CouponCodeStatus_Invalid)),
		)
	if res.RowsAffected <= 0 {
		return errorx.New("更新失败", -1)
	}
	return nil
}

// UpdateTradeData 更新交易数据
func (cp CouponBatch) UpdateTradeData(
	ctx context.Context,
	query *dal.Query,
	batchID string,
	totalAmount float64,
	totalDiscountAmount float64,
	totalOrderNum int,
	avgAmount float64,
	totalUsedNum int64,
) {
	cb := query.CouponBatch
	cb.WithContext(ctx).
		Where(cb.ID.Eq(batchID)).
		UpdateSimple(
			cb.TotalAmount.Value(totalAmount),
			cb.TotalDiscountAmount.Add(totalDiscountAmount),
			cb.TotalOrderNum.Value(totalOrderNum),
			cb.AvgAmount.Value(avgAmount),
			cb.TotalUsedNum.Value(totalUsedNum),
		)
}

type CodeDetailType struct {
	Code              string   `json:"code"`                // 码号
	Status            int      `json:"status"`              // 状态
	CodeType          int      `json:"code_type"`           // 码的类型
	BatchID           string   `json:"batch_id"`            // 所属的批次id
	ValidityStartTime int64    `json:"validity_start_time"` // 有效期开始时间
	ValidityEndTime   int64    `json:"validity_end_time"`   // 有效期结束时间
	Reduce            float64  `json:"reduce"`              // 优惠金额
	Discount          float64  `json:"discount"`            // 优惠折扣
	GoodsIDs          []string `json:"goods_ids"`           // 可使用的商品id
	ExpiredAt         int64    `json:"expired_at"`          // 过期时间
}

// Detail 详情
func (CouponCode) Detail(ctx context.Context, query *dal.Query, code string) (codeDetail *CodeDetailType, err error) {
	codeDetail = &CodeDetailType{}
	// 获取码的信息
	cc := query.CouponCode
	codeRecord, _ := cc.WithContext(ctx).Where(cc.Code.Eq(code)).Take()
	if codeRecord == nil {
		return nil, errorx.New("不存在", -1)
	}
	codeDetail.Code = code
	codeDetail.BatchID = codeRecord.BatchID
	codeDetail.Status = codeRecord.Status
	// 获取批次信息
	cb := query.CouponBatch
	batchRecord, _ := cb.WithContext(ctx).
		Where(cb.ID.Eq(codeRecord.BatchID)).
		Take()
	if batchRecord == nil {
		return nil, errorx.New("无效的优惠码", -1)
	}
	codeDetail.CodeType = batchRecord.CodeType
	codeDetail.ValidityStartTime = batchRecord.ValidityStartTime
	codeDetail.ValidityEndTime = batchRecord.ValidityEndTime
	codeDetail.Reduce = batchRecord.Reduce
	codeDetail.Discount = batchRecord.Discount
	codeDetail.ExpiredAt = codeRecord.ExpiredAt
	return codeDetail, nil
}

// Status 主码的状态
func (CouponCode) Status(
	ctx context.Context,
	query *dal.Query,
	merchantID string,
	code string,
) (status int, err error) {
	cc := query.CouponCode
	record, _ := cc.WithContext(ctx).
		Where(cc.MerchantID.Eq(merchantID)).
		Where(cc.Code.Eq(code)).
		Take()
	if record == nil {
		return 0, errorx.New("无效的码", -1)
	}
	return record.Status, nil
}

// UpdateStatus 修改主码的状态
func (CouponCode) UpdateStatus(
	ctx context.Context,
	query *dal.Query,
	code string,
	status define.StatusType, // 码的状态
) error {
	cc := query.CouponCode
	res, _ := cc.WithContext(ctx).Where(cc.Code.Eq(code)).UpdateSimple(
		cc.Status.Value(int(status)),
	)
	if res.RowsAffected <= 0 {
		return errorx.New("更新状态失败", -1)
	}
	return nil
}

// UpdateExpireTime 更新优惠码的过期时间
func (CouponCode) UpdateExpireTime(
	ctx context.Context,
	query *dal.Query,
	code string,
	expiredAt int64,
) error {
	cc := query.CouponCode
	res, _ := cc.WithContext(ctx).Where(cc.Code.Eq(code)).UpdateSimple(
		cc.ExpiredAt.Value(expiredAt),
	)
	if res.RowsAffected <= 0 {
		return errorx.New("更新状态失败", -1)
	}
	return nil
}

// Reset 普通重置
func (CouponCode) Reset(
	ctx context.Context,
	query *dal.Query,
	code string,
) error {
	ccc := query.CouponChildCode
	record, _ := ccc.WithContext(ctx).Where(ccc.Code.Eq(code)).Where(ccc.Status.Eq(2)).Take()
	if record == nil {
		return errorx.New("重置子码失败", -1)
	}
	res, _ := ccc.WithContext(ctx).
		Where(ccc.ID.Eq(record.ID)).
		UpdateSimple(
			ccc.Status.Value(int(define.CouponChildCodeStatus_UnUsed)),
			ccc.OutTradeNo.Value(""),
		)
	if res.RowsAffected <= 0 {
		return errorx.New("重置子码失败", -1)
	}
	return nil
}

// ExpirationReset 超时重置
func (CouponCode) ExpirationReset(
	ctx context.Context,
	query *dal.Query,
	code string,
	expiration int64,
) error {
	ccc := query.CouponChildCode
	res, _ := ccc.WithContext(ctx).
		Where(ccc.UpdatedAt.Lt(time.Now().Unix()-expiration)).
		Where(ccc.Status.Eq(int(define.CouponChildCodeStatus_Locked))).
		UpdateSimple(
			ccc.Status.Value(int(define.CouponChildCodeStatus_UnUsed)),
			ccc.OutTradeNo.Value(""),
		)
	if res.RowsAffected <= 0 {
		return errorx.New("重置失败", -1)
	}
	return nil
}

type CouponCodeListType struct {
	ID        string `json:"id" gorm:"column:id"`
	Code      string `json:"code" gorm:"column:code"`
	Status    int    `json:"status" gorm:"column:status"`
	CreatedAt int64  `json:"created_at" gorm:"column:created_at"`
}

// List 优惠码列表
func (CouponCode) List(
	ctx context.Context,
	query *dal.Query,
	batchID string, // 批量id
	status define.StatusType, // 状态,1-待使用 2-已使用 3-使用中
	page, limit int,
) (records []CouponCodeListType, total int64, err error) {
	cc := query.CouponCode
	batchDetail, _ := CouponBatch{}.Detail(ctx, query, batchID)
	if batchDetail == nil {
		return []CouponCodeListType{}, 0, errorx.New("无效的批次", -1)
	}
	tx := cc.WithContext(ctx).Where(cc.BatchID.Eq(batchID))
	if status >= 0 {
		tx = tx.Where(cc.Status.Eq(int(status)))
	}
	// 获取优惠码总数
	count, _ := tx.Count()
	// 获取分页数据
	if page > 0 {
		tx = tx.Offset((page - 1) * limit)
	}
	if limit > 0 {
		tx = tx.Limit(limit)
	}
	tx.Select(cc.ID, cc.Code, cc.Status, cc.CreatedAt).Order(cc.CreatedAt.Desc()).Scan(&records)
	for index := range records {
		if batchDetail.ValidityEndTime < time.Now().Unix() {
			records[index].Status = int(define.CouponCodeStatus_Invalid)
		}
	}
	return records, count, nil
}

// CouponCode
type CouponChildCode struct{}

// Create 生成子码
func (CouponChildCode) Create(
	ctx context.Context,
	query *dal.Query,
	records []*models.CouponChildCode,
) error {
	ccc := query.CouponChildCode
	return ccc.WithContext(ctx).CreateInBatches(records, 500)
}

// Take 获取一个子码
func (CouponChildCode) Take(
	ctx context.Context,
	query *dal.Query,
	status define.StatusType,
	code string,
	outTradeNo string,
) (record *models.CouponChildCode, err error) {
	ccc := query.CouponChildCode
	tx := ccc.WithContext(ctx)
	if status != 0 {
		tx = tx.Where(ccc.Status.Eq(int(status)))
	}
	if code != "" {
		tx = tx.Where(ccc.Code.Eq(code))
	}
	if outTradeNo != "" {
		tx = tx.Where(ccc.OutTradeNo.Eq(outTradeNo))
	}
	return tx.Take()
}

// UpdateChildCodeStatus 更新子码状态
func (CouponChildCode) UpdateChildCode(
	ctx context.Context,
	query *dal.Query,
	childCodeID string,
	status define.StatusType,
	outTradeNo string,
) error {
	ccc := query.CouponChildCode
	res, _ := ccc.WithContext(ctx).
		Where(ccc.ID.Eq(childCodeID)).
		Updates(&models.CouponChildCode{
			Status:     int(status),
			OutTradeNo: outTradeNo,
		})
	if res.RowsAffected <= 0 {
		return errorx.New("更新子码状态失败", -1)
	}
	return nil
}

// UpdateChildCodeStatus 更新子码状态
func (CouponChildCode) UpdateChildCodeStatus(
	ctx context.Context,
	query *dal.Query,
	outTradeNo string,
	status define.StatusType,
) error {
	ccc := query.CouponChildCode
	res, _ := ccc.WithContext(ctx).
		Where(ccc.OutTradeNo.Eq(outTradeNo)).
		UpdateSimple(
			ccc.Status.Value(int(status)),
		)
	if res.RowsAffected <= 0 {
		return errorx.New("更新子码状态失败", -1)
	}
	return nil
}

// CouponLog 优惠码核销日志
type CouponLog struct{}

// Create 新增核销日志
func (CouponLog) Create(
	ctx context.Context,
	query *dal.Query,
	BatchID, // 批次
	code string, // 主码
	outTradeNO string, // 交易号
	merchantID string, // 所属商户id
) error {
	cl := query.CouponLog
	return cl.WithContext(ctx).Create(&models.CouponLog{
		MerchantID: merchantID,
		BatchID:    BatchID,
		Code:       code,
		OutTradeNo: outTradeNO,
	})
}

type CouponLogListType struct {
	ID         string `json:"id" gorm:"column:id"`
	Code       string `json:"code" gorm:"column:code"`
	OutTradeNO string `json:"out_trade_no" gorm:"column:out_trade_no"`
	CreatedAt  int64  `json:"created_at" gorm:"column:created_at"`
}

// List 核销日志列表
func (CouponLog) List(
	ctx context.Context,
	query *dal.Query,
	merchantIDs []string,
	batchID, // 批次
	code string, // 主码
	outTradeNO string, // 交易号
	startTime, endTime int64, // 开始时间，结束时间
	page, limit int, // 分页
) (records []CouponLogListType, total int64) {
	cl := query.CouponLog
	tx := cl.WithContext(ctx)
	if len(merchantIDs) > 0 {
		tx = tx.Where(cl.MerchantID.In(merchantIDs...))
	}
	if batchID != "" {
		tx = tx.Where(cl.BatchID.Eq(batchID))
	}
	if code != "" {
		tx = tx.Where(cl.Code.Eq(code))
	}
	if outTradeNO != "" {
		tx = tx.Where(cl.OutTradeNo.Eq(outTradeNO))
	}
	if startTime > 0 && endTime > 0 {
		tx = tx.Where(cl.CreatedAt.Between(startTime, endTime))
	}
	count, _ := tx.Count()
	if page > 0 {
		tx = tx.Offset((page - 1) * limit)
	}
	if limit > 0 {
		tx = tx.Limit(limit)
	}
	tx.Scan(&records)
	if len(records) == 0 {
		records = []CouponLogListType{}
	}
	return records, count
}
